中斷(interrupt)指的是讓 CPU 暫停手上的工作,先處理優先等級更高的項目,而中斷時期的工作都由「中斷服務常式(Interrupt Service Routine, ISR)」程式處理。
ISR 的寫法必須簡單、快速,而且因為 ISR 可能在任何時刻又被打斷(例如遇到優先程度更高的中斷要處理),必須要「可重入(reentrant)」。
所謂「可重入」指的是要可以在任何時刻被中斷,後面再回來繼續執行時依然不能出錯,而要達到可重入性質,ISR 不能調用外部共用資源,以避免造成競爭危害等混亂狀況。
為避免調用外部資源,ISR 的寫法有以下規範:
所以就下列範例來說:
__interrupt double compute_area(double radius) {
double area = PI * radius * radius;
printf(“nArea = %f”, area);
return area;
}
就是非常不好的例子,因為:
double,應該要是 void 才對,所以 return area 也是錯的,因為 ISR 不應該有任何回傳值。double radius 為參數,ISR 不應該有參數傳入。printf(),這個會用到外部的 <stdio> 函式庫。假設真的要計算圓面積,則應該留到主程式處理,如下:
#define PI 3.14159
volatile int flag = 0; // 設定旗標;volatile 意義請參考 Day 11 文章
// 正確的 ISR 寫法
__interrupt void my_isr(void) {
flag = 1; // ISR 只處理旗標
}
int main() {
while (1) {
if (flag) { // 主程式以 ISR 控制的旗標判斷何時展開複雜運算
// 複雜的運算留到主程式寫
double area = PI * 2.0 * 2.0;
printf("Area = %f\n", area);
flag = 0;
}
}
return 0;
}
上述是用 C 語言表示的 ISR 概念程式碼,但真正的 ISR 寫法會因微控制器(Microcontroller Unit, MCU)開發環境而異,例如 Arduino 中,官方文件提及的 ISR 寫法如下:
const byte ledPin = 13;
const byte interruptPin = 2; // input pin that the interruption will be attached to
volatile byte state = LOW; // variable that will be updated in the ISR
void setup() {
pinMode(ledPin, OUTPUT);
pinMode(interruptPin, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(interruptPin), blink, CHANGE);
}
void loop() {
digitalWrite(ledPin, state);
}
void blink() {
state = !state;
}
這當中的:
void blink() {
state = !state;
}
就是 ISR,表示中斷發生時,要切換 state 狀態,是不是超級簡短又乾脆?
至於其他的程式碼在做什麼,老實說寫作的當下我也不懂,如果之後有要寫關於 Arduino 的學習筆記,再來好好理解。